#include <kewinternal.h>

#define SOURCE_FILE_NAME "IOChannel"

#define value(OBJECT) ((GIOChannel *) ((OBJECT)->context[0]))
#define INPUT_BUFFER_SIZE 4096


/* Currently only used by write */
METHOD(Write_Block)
{
  guint bytes_written;
  /* FIXME: check the error */
  GIOError error;
  char *data = KewStringVal(KewSend0(args[0], "string"));

  error = g_io_channel_write(value(receiver), data, strlen(data), &bytes_written);
  results[0] = KewNewString(data + bytes_written);
}

METHOD(Write)
{
  KewObject *send_args[1];
  KewObject *send_results[1];
  KewObject *unwritten_data = args[0];
  KewObject *nothing_to_write;
  KewObject *empty_string = KewNewString("");

  do {
    send_args[0] = unwritten_data;
    Write_Block(receiver, send_args, send_results);
    unwritten_data = send_results[0];

    nothing_to_write = KewSend1(unwritten_data, "=", empty_string);
  } while (KewIsFalse(nothing_to_write));
}

static gboolean
Watch_Callback (GIOChannel *source,
                GIOCondition condition,
                KewObject *watcher_block)
{
  return KewIsTrue(KewSend0(watcher_block, "do"));
}

METHOD(On_Input)
{
  g_io_add_watch(value(receiver),
                 G_IO_IN,
                 (GIOFunc) Watch_Callback,
                 (gpointer) args[0]);
}

METHOD(On_Output)
{
  g_io_add_watch(value(receiver),
                 G_IO_OUT,
                 (GIOFunc) Watch_Callback,
                 (gpointer) args[0]);
}

METHOD(Read)
{
  char buffer[INPUT_BUFFER_SIZE+1];
  guint bytes_read;
  GIOError error = g_io_channel_read(value(receiver),
                                     buffer,
                                     INPUT_BUFFER_SIZE,
                                     &bytes_read);
  buffer[bytes_read] = '\0';
  results[0] = KewNewString(buffer);
}

METHOD(ReadAll)
{
  KewObject *empty;
  KewObject *empty_string = KewWrapString("");
  KewObject *all_data = empty_string;
  KewObject *more_data[1];

  do {
    Read(receiver, args, more_data);
    empty = KewSend1(more_data[0], "=", empty_string);
    all_data = KewSend1(all_data, ",", more_data[0]);
  } while (KewIsFalse(empty));

  results[0] = all_data;
}

METHOD(Close)
{
  g_io_channel_close(value(receiver));
}

static KewObjectType io_channel_type = {
  SOURCE_FILE_NAME, __LINE__,
  1,
  {
    {"=", Kew__GenericEqual, 1, 1},
    {"hash", Kew__GenericHash, 0, 1},
    {"write", Write, 1, 0},
    {"read", Read, 0, 1},
    {"readAll", ReadAll, 0, 1},
    {"onInput", On_Input, 1, 0},
    {"onOutput", On_Output, 1, 0},
    {"close", Close, 0, 0},
    {NULL, NULL, 0, 0}
  }
};


KewObject *
KewNewUnixIOChannel(int fd)
{
  KewObject *result = KewNewObject(&io_channel_type);
  value(result) = g_io_channel_unix_new(fd);
  return result;
}


KewObject *
KewNewIOChannel(GIOChannel *channel)
{
  KewObject *result = KewNewObject(&io_channel_type);
  value(result) = channel;
  return result;
}
